package term

import (
	"context"
	// "fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/product/model"
	"mall/service/product/rpc/internal/svc"
	"mall/service/product/rpc/internal/utils"
	"mall/service/product/rpc/types/product"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetTermListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetTermListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTermListLogic {
	return &GetTermListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetTermListLogic) GetTermList(in *product.BaseListReq) (*product.TermListResp, error) {
	if in.Filter == nil || len(*in.Filter) == 0 {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)
	// logrus.Info(fmt.Sprintf("GetTermList preloadMap: %+v", preloadMap))
	// map[productMedia:[Uid Url] productTerm:[Id Name Slug Content] productTermMeta:[mediaId mediaUrl]]

	// logrus.Info(fmt.Sprintf("GetTermList eagerLoadMap: %+v", eagerLoadMap))
	// map[term_id:map[in:[6 12]]]

	productTermEntityName := model.RpcEntityNames.ProductTerm
	if _, ok := model.RpcEntityPreloadMap[productTermEntityName]; !ok {
		return &product.TermListResp{}, nil
	}

	productTermRepositoryName := model.RepositoryNames.ProductTerm
	columnMap, ok := model.RepositoryPreloadMap[productTermRepositoryName]
	if !ok {
		return &product.TermListResp{}, nil
	}

	var selectCols []string
	if preloadFields, ok := preloadMap[productTermEntityName]; ok {
		selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
	}

	filterMap := modelx.ParseFilter(in.Filter, columnMap)
	// logrus.Info(fmt.Sprintf("GetTermList filterMap: %+v", filterMap))
	// map[term_id:map[in:[6 12]]]
	if filterMap == nil {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	filterMap[model.ProductTermColumns.Status] = map[string][]any{
		"equalTo": {globalkey.StatusEnabled},
	}

	orderBy := modelx.ParseSorter(in.Sorter, columnMap)
	// logrus.Info(fmt.Sprintf("GetTermList orderBy: %s", orderBy))

	var keyword string
	if in.Search != nil && len(strings.TrimSpace(*in.Search)) > 0 {
		keyword = strings.TrimSpace(*in.Search)
	}

	res, count, err := l.svcCtx.CurrentProductTerm.GetList(l.ctx, model.M{
		"offset":  in.Offset,
		"limit":   in.Limit,
		"search":  keyword,
		"orderBy": orderBy,
		"select":  strings.Join(selectCols, ","),
	}, 2, filterMap)
	if err != nil {
		return nil, err
	}

	hasNextPage, hasPreviousPage := modelx.ParseCount(count)
	resp := &product.TermListResp{
		HasNextPage:     &hasNextPage,
		HasPreviousPage: &hasPreviousPage,
	}

	var total int64
	if len(res) > 0 {
		var ids []any
		for _, v := range res {
			ids = append(ids, v.TermID)
		}

		subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
		if eagerLoadEntities, ok := eagerLoadMap[productTermEntityName]; ok {
			for _, entity := range eagerLoadEntities {
				if entity == "total" {
					_, total, err = l.svcCtx.CurrentProductTerm.GetList(l.ctx, model.M{}, 0, filterMap)
					if err != nil {
						total = 0
					}
					continue
				}

				if entitySettingMap, ok := model.RpcEntityPreloadMap[entity]; ok {
					subEntitySettingMap[entity] = entitySettingMap
				}
			}
		}

		idMetaMap := make(map[uint64][]*product.MetaInfo)
		productTermMetaEntityName := model.RpcEntityNames.ProductTermMeta
		if entitySettingMap, ok := subEntitySettingMap[productTermMetaEntityName]; ok {
			if subPreloadFields, ok := preloadMap[productTermMetaEntityName]; ok {
				var metaKeys []any
				metaKeyMap := make(map[string]string)
				for _, field := range subPreloadFields {
					if fieldSetting, ok := entitySettingMap[field]; ok && len(fieldSetting.Name) > 0 {
						metaKeys = append(metaKeys, fieldSetting.Name)
						metaKeyMap[fieldSetting.Name] = field
					}
				}
				if len(metaKeys) > 0 {
					subFilterMap := make(map[string]map[string][]any)
					subFilterMap[model.ProductTermMetumColumns.TermID] = map[string][]any{
						"in": ids,
					}
					subFilterMap[model.ProductTermMetumColumns.MetaKey] = map[string][]any{
						"in": metaKeys,
					}
					var orderBy string = model.ProductTermMetumColumns.MetaID + " DESC"
					list, _, err := l.svcCtx.CurrentProductTerm.GetMeta(l.ctx, model.M{
						"orderBy": orderBy,
					}, -1, subFilterMap)
					if err != nil && err != model.ErrNotFound {
						return nil, err
					}

					if len(list) > 0 {
						keyMap := make(map[uint64]map[string]struct{})
						for _, v := range list {
							key := v.MetaKey
							if len(key) > 0 && v.MetaValue.Valid {
								if subMap, ok := keyMap[v.MetaID]; ok { // 防重复
									if _, ok := subMap[key]; ok {
										continue
									}

									keyMap[v.MetaID][v.MetaKey] = struct{}{}
								} else {
									keyMap[v.MetaID] = make(map[string]struct{})
								}

								if field, ok := metaKeyMap[key]; ok {
									idMetaMap[v.TermID] = append(idMetaMap[v.TermID], &product.MetaInfo{
										Id:    pointy.GetPointer(v.MetaID),
										Key:   pointy.GetPointer(field),
										Value: pointy.GetPointer(v.MetaValue.String),
									})
								}
							}
						}
					}
				}
			}
		}

		idMediaMap := make(map[uint64][]*product.MediaInfo)
		productMediaEntityName := model.RpcEntityNames.ProductMedia
		if _, ok := subEntitySettingMap[productMediaEntityName]; ok {
			productMediaRepositoryName := model.RepositoryNames.ProductMedia
			subPreloadFields, hasSubPreloadFields := preloadMap[productMediaEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productMediaRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.ProductAttachmentRelationshipColumns.ObjectID] = map[string][]any{
					"in": ids,
				}
				subFilterMap[model.ProductAttachmentRelationshipColumns.ObjectType] = map[string][]any{
					"moreThan": {globalkey.AttachmentLogTypeTermOffset},
				}
				subFilterMap[model.ProductAttachmentRelationshipColumns.AttachmentThumbnail] = map[string][]any{
					"equalTo": {1},
				}
				subSelectCols = append(subSelectCols, model.ProductAttachmentRelationshipColumns.ObjectID)
				var orderBy string = model.ProductAttachmentRelationshipColumns.AttachmentOrder
				list, _, err := l.svcCtx.CurrentProductTerm.GetMedia(l.ctx, model.M{
					"orderBy": orderBy,
					"select":  strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &product.MediaInfo{
							Uid:       &v.AttachmentRelationshipID,
							Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
							Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
							Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
							Uri:       &v.AttachmentSource,
							Url:       &v.AttachmentImageURL,
							AltText:   &v.AttachmentImageAlt,
							MetaData:  &v.AttachmentImageMeta,
						})
					}
				}
			}
		}

		for _, v := range res {
			var media []*product.MediaInfo
			if ms, ok := idMediaMap[v.TermID]; ok {
				media = ms[:]
			}
			var meta []*product.MetaInfo
			if ms, ok := idMetaMap[v.TermID]; ok {
				meta = ms[:]
			}
			resp.List = append(resp.List, &product.TermInfo{
				Id:        &v.TermID,
				Name:      &v.Name,
				Slug:      &v.Slug,
				Taxonomy:  pointy.GetPointer(utils.FormatTaxonomy(int32(v.Taxonomy))),
				ParentId:  &v.ParentID,
				GroupId:   &v.TermGroup,
				Count:     pointy.GetPointer(uint32(v.Count)),
				Desc:      &v.Description,
				Sort:      pointy.GetPointer(uint32(v.Sort)),
				Status:    pointy.GetPointer(uint32(v.Status)),
				CreatedAt: pointy.GetPointer(modelx.FormatTime(v.CreatedAt, "")),
				UpdatedAt: pointy.GetPointer(modelx.FormatTime(v.UpdatedAt, "")),
				Meta:      meta,
				Media:     media,
			})
		}
	}
	resp.Total = uint64(total)

	return resp, nil
}
